<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JSON生成Java实体类工具</title>
    <style>
        * {
            box-sizing: border-box;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            background-color: #f5f7fa;
            padding: 20px;
            color: #333;
        }
        .container {
            background-color: white;
            padding: 2rem;
            border-radius: 10px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
            width: 95%;
            max-width: 1200px;
            display: flex;
            flex-direction: column;
            height: 90vh;
        }
        h1 {
            text-align: center;
            margin-top: 0;
            color: #2c3e50;
            margin-bottom: 1.5rem;
            font-size: 1.8rem;
        }
        .main-content {
            display: flex;
            gap: 2rem;
            flex: 1;
            min-height: 0; /* 允许子元素滚动 */
        }
        @media (max-width: 768px) {
            .main-content {
                flex-direction: column;
            }
        }
        .panel {
            flex: 1;
            display: flex;
            flex-direction: column;
            border: 1px solid #e1e5eb;
            border-radius: 8px;
            overflow: hidden;
            background-color: #fafcff;
        }
        .panel-header {
            background-color: #4a6fa5;
            color: white;
            padding: 1rem;
            text-align: center;
            font-weight: bold;
        }
        .panel.encode .panel-header { background-color: #28a745; }
        .panel.decode .panel-header { background-color: #17a2b8; }
        
        .input-area, .output-area {
            display: flex;
            flex-direction: column;
            flex: 1;
            padding: 1rem;
			overflow-y: auto;
        }
        label {
            display: block;
            margin-bottom: 0.5rem;
            font-weight: 600;
            color: #495057;
        }
        textarea, .result {
            flex: 1;
            width: 100%;
            padding: 0.75rem;
            border: 1px solid #ced4da;
            border-radius: 4px;
            box-sizing: border-box;
            font-family: 'Consolas', 'Courier New', monospace;
            resize: none;
            margin-bottom: 1rem;
            background-color: #fff;
        }
        textarea:focus, .result:focus {
             outline: none;
             border-color: #4a6fa5;
             box-shadow: 0 0 0 3px rgba(74, 111, 165, 0.25);
        }
        .result {
            background-color: #f8f9fa;
            overflow: auto;
            white-space: pre;
        }
        .config-area {
             padding: 0 1rem 1rem 1rem;
             background-color: #f1f4f8;
             border-top: 1px solid #e1e5eb;
        }
        .form-group {
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            margin-bottom: 0.5rem;
        }
        .form-group label {
            min-width: 100px;
            margin-bottom: 0;
            display: flex;
            align-items: center;
        }
        .form-group input, .form-group select {
            flex: 1;
            padding: 0.5rem;
            border: 1px solid #ced4da;
            border-radius: 4px;
        }
        .button-group {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            padding: 0 1rem 1rem 1rem;
        }
        button {
            padding: 0.6rem 1.2rem;
            background-color: #4a6fa5;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            flex: 1;
            min-width: 100px;
            font-size: 0.95rem;
            font-weight: 600;
            transition: background-color 0.2s;
        }
        button:hover {
            background-color: #3a5a80;
        }
        .panel.encode button.generate { background-color: #28a745; }
        .panel.encode button.generate:hover { background-color: #218838; }
        
        .panel.decode button.copy-btn { background-color: #6c757d; }
        .panel.decode button.copy-btn:hover { background-color: #5a6268; }
        
        .error {
            color: #dc3545;
            font-size: 0.85rem;
            margin-top: 0.25rem;
            min-height: 1.2rem;
            padding: 0 1rem;
        }
        .hint {
            font-size: 0.8rem;
            color: #6c757d;
            margin-top: 0.25rem;
            text-align: center;
            padding: 0 1rem 1rem 1rem;
        }
        .status {
            text-align: center;
            padding: 0.5rem;
            font-weight: bold;
        }
        .status.success { color: #28a745; }
        .status.error { color: #dc3545; }
    </style>
</head>
<body>

<div class="container">
    <h1>JSON生成Java实体类工具</h1>
    <div class="main-content">
        <!-- JSON输入与配置面板 -->
        <div class="panel encode">
            <div class="panel-header">JSON输入与配置</div>
            <div class="input-area">
                <label for="jsonInput">输入JSON:</label>
                <textarea id="jsonInput" placeholder="粘贴您的JSON数据...">{
  "id": 12345,
  "name": "张三",
  "email": "zhangsan@example.com",
  "isActive": true,
  "balance": 1234.56,
  "tags": ["vip", "premium"],
  "profile": {
    "age": 30,
    "address": {
      "street": "中山路123号",
      "city": "广州"
    }
  },
  "loginHistory": [
    {
      "timestamp": "2023-10-27T08:00:00Z",
      "ip": "192.168.1.1"
    },
    {
      "timestamp": "2023-10-28T10:30:00Z",
      "ip": "192.168.1.2"
    }
  ]
}</textarea>
                <div class="error" id="jsonError"></div>
            </div>
            
            <div class="config-area">
                <div class="form-group">
                    <label for="className">类名:</label>
                    <input type="text" id="className" value="User" placeholder="例如: User">
                </div>
                <div class="form-group">
                    <label for="packageName">包名:</label>
                    <input type="text" id="packageName" value="com.example.model" placeholder="例如: com.example.model">
                </div>
                <div class="form-group">
                    <label for="indentType">缩进:</label>
                    <select id="indentType">
                        <option value="4">4个空格</option>
                        <option value="2">2个空格</option>
                        <option value="tab">Tab</option>
                    </select>
                </div>
                <div class="form-group">
                    <label for="generateGetterSetter">Getter/Setter:</label>
                    <select id="generateGetterSetter">
                        <option value="true">生成</option>
                        <option value="false">不生成</option>
                    </select>
                </div>
            </div>
            
            <div class="button-group">
                <button id="generateBtn" class="generate">生成Java类</button>
                <button id="copyJsonBtn">复制JSON</button>
                <button id="clearJsonBtn">清空</button>
            </div>
            <div class="hint">支持嵌套对象、数组。确保JSON格式正确。</div>
        </div>

        <!-- Java代码输出面板 -->
        <div class="panel decode">
            <div class="panel-header">Java实体类代码</div>
            <div class="output-area">
                <label for="javaResult">生成的Java代码:</label>
                <pre id="javaResult" class="result" readonly></pre>
                 <div class="status" id="generationStatus"></div>
            </div>
            <div class="button-group">
                <button id="copyJavaBtn" class="copy-btn">复制代码</button>
                <button id="downloadJavaBtn">下载.java文件</button>
                <button id="clearJavaBtn">清空</button>
            </div>
            <div class="hint">代码基于Jackson注解，支持List和嵌套对象。</div>
        </div>
    </div>
</div>

<script>
    // DOM 元素
    const jsonInput = document.getElementById('jsonInput');
    const jsonError = document.getElementById('jsonError');
    const classNameInput = document.getElementById('className');
    const packageNameInput = document.getElementById('packageName');
    const indentTypeSelect = document.getElementById('indentType');
    const generateGetterSetterSelect = document.getElementById('generateGetterSetter');
    const generateBtn = document.getElementById('generateBtn');
    const copyJsonBtn = document.getElementById('copyJsonBtn');
    const clearJsonBtn = document.getElementById('clearJsonBtn');

    const javaResult = document.getElementById('javaResult');
    const generationStatus = document.getElementById('generationStatus');
    const copyJavaBtn = document.getElementById('copyJavaBtn');
    const downloadJavaBtn = document.getElementById('downloadJavaBtn');
    const clearJavaBtn = document.getElementById('clearJavaBtn');

    // 确定缩进字符串
    function getIndentString() {
        const type = indentTypeSelect.value;
        if (type === 'tab') return '\t';
        return ' '.repeat(parseInt(type));
    }

    // 首字母大写
    function capitalize(str) {
        if (!str) return str;
        return str.charAt(0).toUpperCase() + str.slice(1);
    }

    // 根据JSON值推断Java类型
    function inferJavaType(value, isArrayElement = false) {
        if (value === null) {
            // 对于null，我们假设它可能是Object，或者根据上下文处理
            // 在数组中，我们更倾向于推断为Object，因为数组可能包含不同类型的元素
            // 但为了简化，我们统一返回Object
            return "Object";
        }
        const type = typeof value;
        if (type === 'string') {
            // 尝试解析是否为日期格式 (ISO 8601)
            // 这是一个简化的检查
            if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?(Z|[+-]\d{2}:\d{2})?$/.test(value)) {
                 return "Date";
            }
            return "String";
        }
        if (type === 'number') {
            if (Number.isInteger(value)) {
                // 简单区分 int 和 long，实际应用中可能需要更复杂的逻辑
                if (Math.abs(value) <= Math.pow(2, 31) - 1) {
                    return "Integer";
                } else {
                    return "Long";
                }
            } else {
                return "Double"; // 或 Float
            }
        }
        if (type === 'boolean') {
            return "Boolean";
        }
        if (Array.isArray(value)) {
            if (value.length > 0) {
                // 递归推断数组元素类型
                const elementType = inferJavaType(value[0], true);
                return `List<${elementType}>`;
            } else {
                // 空数组，无法推断，使用Object
                return "List<Object>";
            }
        }
        if (type === 'object') {
             // 对于普通对象，我们将递归处理
             // 这里返回一个占位符，稍后会被替换为实际的类名
             // 在主逻辑中，我们会为每个对象创建一个类
             // 为了简化，我们使用键名作为类名（首字母大写）
             // 但这在主逻辑中处理更合适。这里先返回Object或Map
             // 更好的方式是在主遍历中处理
             // 暂时返回Object，主逻辑会覆盖
             return "Object"; // This will be replaced
        }
        return "Object"; // Fallback
    }


    // 递归处理JSON对象，生成类定义
    function processJsonObject(obj, className, classes, indent, generateGetSet) {
        if (classes[className]) {
            // 如果类已经处理过（防止无限循环或重复处理），则跳过
            return;
        }

        classes[className] = ""; // 占位，防止循环引用导致的无限递归
        let classCode = "";
        const indent1 = indent;
        const indent2 = indent + indent;
        const indent3 = indent + indent + indent;

        classCode += `public class ${className} {\n\n`;

        const fields = [];
        const fieldInitializers = []; // 存储需要初始化的字段（如List）

        for (const key in obj) {
            if (obj.hasOwnProperty(key)) {
                const value = obj[key];
                let javaType = inferJavaType(value);

                // 处理嵌套对象
                if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
                    const nestedClassName = capitalize(key); // 简单命名策略
                    javaType = nestedClassName;
                    // 递归处理嵌套对象
                    processJsonObject(value, nestedClassName, classes, indent, generateGetSet);
                }
                // 处理对象数组
                else if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'object' && value[0] !== null) {
                    const nestedClassName = capitalize(key.slice(0, -1)); // 去掉复数s (简单策略)
                    javaType = `List<${nestedClassName}>`;
                    // 递归处理数组中的对象类型
                    processJsonObject(value[0], nestedClassName, classes, indent, generateGetSet);
                }

                fields.push({ name: key, type: javaType });
                // 如果是List类型，需要初始化
                if (javaType.startsWith("List<")) {
                    fieldInitializers.push(key);
                }
            }
        }

        // 生成字段
        fields.forEach(field => {
            classCode += `${indent1}private ${field.type} ${field.name};\n`;
        });

        // 生成构造函数
        classCode += `\n${indent1}public ${className}() {\n`;
        if (fieldInitializers.length > 0) {
            fieldInitializers.forEach(fieldName => {
                classCode += `${indent2}this.${fieldName} = new ArrayList<>();\n`;
            });
        }
        classCode += `${indent1}}\n`;

        // 生成 Getter 和 Setter
        if (generateGetSet === 'true') {
            fields.forEach(field => {
                const capitalizedFieldName = capitalize(field.name);
                // Getter
                classCode += `\n${indent1}public ${field.type} get${capitalizedFieldName}() {\n`;
                classCode += `${indent2}return ${field.name};\n`;
                classCode += `${indent1}}\n`;
                // Setter
                classCode += `\n${indent1}public void set${capitalizedFieldName}(${field.type} ${field.name}) {\n`;
                classCode += `${indent2}this.${field.name} = ${field.name};\n`;
                classCode += `${indent1}}\n`;
            });
        }

        classCode += "}\n";
        classes[className] = classCode;
    }


    // 主生成函数
    function generateJavaClass() {
        const jsonStr = jsonInput.value.trim();
        const className = classNameInput.value.trim() || 'Example';
        const packageName = packageNameInput.value.trim();
        const indentStr = getIndentString();
        const generateGetSet = generateGetterSetterSelect.value;

        jsonError.textContent = '';
        generationStatus.textContent = '';
        generationStatus.className = 'status';
        javaResult.textContent = '';

        if (!jsonStr) {
            jsonError.textContent = '请输入JSON数据';
            return;
        }

        let jsonObj;
        try {
            jsonObj = JSON.parse(jsonStr);
        } catch (e) {
            jsonError.textContent = 'JSON格式错误: ' + e.message;
            return;
        }

        if (typeof jsonObj !== 'object' || jsonObj === null || Array.isArray(jsonObj)) {
             jsonError.textContent = 'JSON根元素必须是一个对象';
             return;
        }

        try {
            // 存储所有生成的类
            const classes = {};
            
            // 处理主类和嵌套类
            processJsonObject(jsonObj, className, classes, indentStr, generateGetSet);

            // 组合所有类的代码
            let fullCode = "";
            if (packageName) {
                fullCode += `package ${packageName};\n\n`;
            }
            
            // Jackson imports
            fullCode += `import com.fasterxml.jackson.annotation.JsonProperty;\n`;
            fullCode += `import java.util.List;\n`;
            fullCode += `import java.util.ArrayList;\n`;
            fullCode += `import java.util.Date; // Ensure Jackson can serialize/deserialize Date\n\n`;

            // 按顺序添加类（主类在前）
            fullCode += classes[className]; // Add main class first
            delete classes[className]; // Remove it from the rest
            
            for (const cls in classes) {
                if (classes.hasOwnProperty(cls)) {
                    fullCode += `\n\n${classes[cls]}`;
                }
            }
            
            javaResult.textContent = fullCode;
            generationStatus.textContent = 'Java类生成成功!';
            generationStatus.className = 'status success';

        } catch (e) {
            console.error("生成Java类时出错:", e);
            generationStatus.textContent = '生成失败: ' + (e.message || '未知错误');
            generationStatus.className = 'status error';
        }
    }


    // 复制到剪贴板
    async function copyToClipboard(text, button, successMessage = "已复制!") {
        if (!text) return;
        try {
            await navigator.clipboard.writeText(text);
            const originalText = button.textContent;
            button.textContent = successMessage;
            setTimeout(() => { button.textContent = originalText; }, 2000);
        } catch (err) {
            console.error('复制失败: ', err);
        }
    }

    // 下载文件
    function downloadFile(filename, content) {
        const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }


    // 事件监听
    generateBtn.addEventListener('click', generateJavaClass);
    copyJsonBtn.addEventListener('click', () => copyToClipboard(jsonInput.value, copyJsonBtn));
    copyJavaBtn.addEventListener('click', () => copyToClipboard(javaResult.textContent, copyJavaBtn, "代码已复制!"));
    
    downloadJavaBtn.addEventListener('click', () => {
        const className = classNameInput.value.trim() || 'Example';
        const code = javaResult.textContent;
        if (code) {
            downloadFile(`${className}.java`, code);
        }
    });

    clearJsonBtn.addEventListener('click', () => {
        jsonInput.value = '';
        jsonError.textContent = '';
    });
    clearJavaBtn.addEventListener('click', () => {
        javaResult.textContent = '';
        generationStatus.textContent = '';
        generationStatus.className = 'status';
    });


    // 初始化示例
    window.addEventListener('load', generateJavaClass);

</script>

</body>
</html>
